#include <QDateTime>
#include <QDebug>

#include "counter_cmd.h"

QStringList NOTE_LIST = {
	"CNY100",
	" CNY50",
	" CNY20",
	" CNY10",
	"  CNY5",
	"  CNY1",
	"",
	"",
	"",
	""
};

QByteArray cntr_pack_command(uint32_t cmd, uint32_t para)
{
	QByteArray ba;
                                    
    unsigned char cmd1[] = {
    	0xa1, 0xa2, 0xa3, 0xa4,			// STX(4 bytes)
    	0x04,							// Size(1 bytes)
    	0x32,							// Cmd(1 Byte)
    	0xbb, 0xbb,						// ETX(2 Bytes)
    	0x00							// BCC(1 Byte)
    };
    unsigned char cmd2[] = {
    	0xa1, 0xa2, 0xa3, 0xa4,			// STX(4 bytes)
    	0x08,							// Size(1 bytes)
    	0x22,							// Cmd(1 Byte)
    	0xff, 0xff, 0xff, 0xff,			// Data(4 Bytes)
    	0xbb, 0xbb,						// ETX(2 Bytes)
    	0x00							// BCC(1 Byte)
    };
    unsigned char cmd3[] = {
    	0xa1, 0xa2, 0xa3, 0xa4,			// STX(4 bytes)
    	0x12,							// Size(1 bytes)
    	0x21,							// Cmd(1 Byte)
    	0x32,0x30,0x31,0x38,0x30,0x34,0x32,0x36,0x31,0x30,0x31,0x31,0x31,0x35,			// Data(14 Bytes)
    	0xbb, 0xbb,						// ETX(2 Bytes)
    	0x00							// BCC(1 Byte)
    };
    unsigned char cmd4[] = {
    	0xa1, 0xa2, 0xa3, 0xa4,			// STX(4 bytes)
    	0x05,							// Size(1 bytes)
    	0x02,							// Cmd(1 Byte)
    	0x00,							// Data(1 Byte)
    	0xbb, 0xbb,						// ETX(2 Bytes)
    	0x00							// BCC(1 Byte)
    };

	switch (cmd) {
	case CNTR_COMM_TEST_CMD:
	case CNTR_FAULT_CLEAR_CMD:
	case CNTR_EXIT_WORK_MODE_CMD:
	case CNTR_PAUSE_COUNT_CMD:
	case CNTR_STORE_NOTE_CMD:
	case CNTR_COMPLET_STORE_NOTE_CMD:
	case CNTR_CLEAR_TRANSACTION_CMD:
	case CNTR_OPEN_SHELL_CMD:
	case CNTR_CLOSE_SHELL_CMD:
	case CNTR_ENTER_COIN_MODE_CMD:
	case CNTR_EXIT_COIN_MODE_CMD:
		cmd1[5] = cmd;
		ba = QByteArray((char *) cmd1, 9);
		break;

	case CNTR_START_COUNT_CMD:
		ba = QByteArray((char *) cmd2, 13);
		break;
	case CNTR_ENTER_WORK_MODE_CMD:
		ba = QByteArray((char *) cmd3, 23);
        ba.replace(6, 14, QDateTime::currentDateTime().toString("yyyyMMddhhmmss").toLatin1());
		break;

	case CNTR_MODE_SELECT:
		ba = QByteArray((char *) cmd4, 10);
        ba[6] = para & 0xff;
		break;
	}

	// calculate the checksum of command package
    unsigned char cksum = 0;
    for (int idx = 0; idx < ba.length() - 1; idx++)
        cksum ^= ba[idx];
    ba[ba.length() - 1] = cksum;

	return ba;
}

QByteArray cntr_parse_response(QByteArray resp)
{
	QByteArray act_resp, resp_data;
	uint16_t cnt;
	uint16_t status;

    unsigned char stx1[] = {
    	0xa1, 0xa2, 0xa3, 0xa4,			// STX(4 bytes)
    	0x00
    };
    unsigned char stx2[] = {
    	0xb1, 0xb2, 0xb3, 0xb4,			// STX(4 bytes)
    	0x00
    };

	while (resp.length() >= 9) {
		if (!resp.startsWith((char *) stx1) && !resp.startsWith((char *) stx2)) {
			resp.remove(0, 1);
			continue;
		}
        uint8_t resp_length, resp_code;
		if (resp.startsWith((char *) stx1)) {
			// Response with STX A1 A2 A3 A4
			// 1 bytes packet size
			resp_length = resp[4];
			if (resp_length + 5 < resp.length())
				break;
			// Response packet
			act_resp = resp.mid(0, resp_length + 5);
			resp.remove(0, resp_length + 5);
			// Response code
			resp_code = act_resp[5];
			// Reponse data
		    resp_data = act_resp.mid(6, act_resp.length() - 9);

			// check the checksum of response
		    unsigned char cksum = 0;
		    for (int idx = 0; idx < act_resp.length() - 1; idx++)
		        cksum ^= (uint8_t) act_resp[idx];
		    if (cksum != (uint8_t) act_resp[act_resp.length() - 1]) {
		    	qDebug() << "Response Checksum Error  " << act_resp;
		    	continue;
	    	}
		}
		else {
			// Response with STX B1 B2 B3 B4
			// 2 bytes packet size
			resp_length = resp[4] + (resp[5] << 8);
			if (resp_length + 6 < resp.length())
				break;
			// Response packet
			act_resp = resp.mid(0, resp_length + 6);
			resp.remove(0, resp_length + 6);
			// Response code
			resp_code = act_resp[6];
			// Reponse data
		    resp_data = act_resp.mid(7, act_resp.length() - 10);

			// check the checksum of response
		    unsigned char cksum = 0;
		    for (int idx = 0; idx < act_resp.length() - 1; idx++)
		        cksum ^= (uint8_t) act_resp[idx];
		    cksum = ((cksum & 0x0f) << 4) + ((cksum & 0xf0) >> 4);
		    uint8_t act_cksum = (uint8_t) act_resp[act_resp.length() - 1];
		    if (cksum != act_cksum) {
		    	qInfo() << "Response Checksum Error  " << QString("%1 %2 ").arg(cksum, 2, 16, QLatin1Char('0')).arg(act_cksum, 2, 16, QLatin1Char('0'));
		    }
		}

	    switch (resp_code) {
	    case CNTR_COMM_TEST_CMD:
	    case CNTR_FAULT_CLEAR_CMD:
		case CNTR_PAUSE_COUNT_CMD:
	    	break;
	    case CNTR_EXIT_WORK_MODE_CMD:
	    case CNTR_ENTER_WORK_MODE_CMD:
	    case CNTR_START_COUNT_CMD:
	    case CNTR_CLEAR_TRANSACTION_CMD:
	    case CNTR_OPEN_SHELL_CMD:
	    case CNTR_CLOSE_SHELL_CMD:
	    case CNTR_ENTER_COIN_MODE_CMD:
	    case CNTR_EXIT_COIN_MODE_CMD:
            if (resp_data.length() != 1) {
	    		qDebug() << "Response Data Error";
	    		break;
            }
            qInfo() << "Response Status: " << QString("%1").arg((uint16_t) resp_data[0], 2, 16, QLatin1Char('0'));
            status = (uint8_t) resp_data[0];
	    	break;

	    case CNTR_COUNT_INFO_RESPONSE1:
	    case CNTR_COUNT_INFO_RESPONSE2:
	    	qInfo() << "Count Info";
	    	if (resp_data.length() != 44) {
	    		qDebug() << "\tResponse Data Error";
	    		break;
	    	}
	    	qInfo() << "\tCounter Status: " << QString("%1").arg((uint16_t) resp_data[0], 2, 16, QLatin1Char('0'));
            status = (uint8_t) resp_data[0];
	    	if ((uint16_t) resp_data[1] == 0x01)
	    		qInfo() << "\tRMB";
	    	else
	    		qInfo() << "\tUnknown Note";
	    	for (int k = 0; k < 10; k++) {
	    		cnt = (uint16_t) resp_data[2 + 2 * k];
	    		cnt += ((uint16_t) resp_data[2 + 2 * k + 1]) << 8;
	    		if (cnt)
	    			qInfo() << "\t" << NOTE_LIST[k] << " " << cnt;
	    	}
	    	for (int k = 0; k < 10; k++) {
	    		cnt = (uint16_t) resp_data[2 + 20 + 2 * k];
	    		cnt += ((uint16_t) resp_data[2 + 20 + 2 * k + 1]) << 8;
	    		if (cnt)
	    			qInfo() << "\t" << NOTE_LIST[k] << " " << cnt;
	    	}
	    	if ((uint16_t) resp_data[2 + 20 + 2 * 10])
	    		qInfo() << "\tRejected Note: " << QString("%1").arg((uint16_t) resp_data[2 + 20 + 2 * 10]);
            qInfo() << "\tSystem Status:" << QString("%1").arg((uint16_t) resp_data[2 + 20 + 2 * 10 + 1]);
	    	break;
	    case CNTR_FAULT_REPORT_CMD_RESPONSE:
	    	break;
	    case CNTR_ONLINE_CHECK_RESPONSE:
	    	break;
	    case CNTR_NOTESERIAL_REPORT_RESPONSE:
	    	qInfo() << "Note Serial Info";
	    	cnt = (uint16_t) resp_data[0];
	    	cnt += ((uint16_t) resp_data[1]) << 8;
	    	if (resp_data.length() != cnt * 25 + 2) {
	    		qInfo() << "\tResponse Data Error";
	    		break;
	    	}
	    	qInfo() << "\tNote Count: " << cnt;
	    	for (int k = 0; k < cnt; k++) {
	    		qInfo() << "\t" << resp_data.mid(2 + k * 25, 25);
	    	}
	    	break;
	    case CNTR_STORE_IN_PROGRESS_RESPONSE:
	    	qInfo() << "Store in Progress";
            if (resp_data.length() != 1) {
	    		qDebug() << "\tResponse Data Error";
	    		break;
            }
            qInfo() << "\tResponse Status: " << QString("%1").arg((uint16_t) resp_data[0], 2, 16, QLatin1Char('0'));
            status = (uint8_t) resp_data[0];
	    	break;
	    case CNTR_COMPLETE_STORE_RESPONSE:
	    	qInfo() << "Store Complete";
	    	if (resp_data.length() != 43) {
	    		qDebug() << "\tResponse Data Error";
	    		break;
	    	}
	    	qInfo() << "\tCounter Status: " << QString("%1").arg((uint16_t) resp_data[0], 2, 16, QLatin1Char('0'));
            status = (uint8_t) resp_data[0];
	    	if ((uint16_t) resp_data[1] == 0x01)
	    		qInfo() << "\tRMB";
	    	else
	    		qInfo() << "\tUnknown Note";
	    	for (int k = 0; k < 10; k++) {
	    		cnt = (uint16_t) resp_data[2 + 2 * k];
	    		cnt += ((uint16_t) resp_data[2 + 2 * k + 1]) << 8;
	    		if (cnt)
	    			qInfo() << "\t" << NOTE_LIST[k] << " " << cnt;
	    	}
	    	for (int k = 0; k < 10; k++) {
	    		cnt = (uint16_t) resp_data[2 + 20 + 2 * k];
	    		cnt += ((uint16_t) resp_data[2 + 20 + 2 * k + 1]) << 8;
	    		if (cnt)
	    			qInfo() << "\t" << NOTE_LIST[k] << " " << cnt;
	    	}
	    	if ((uint16_t) resp_data[2 + 20 + 2 * 10])
	    		qInfo() << "\tRejected Note: " << QString("%1").arg((uint16_t) resp_data[2 + 20 + 2 * 10]);
	    	break;
	    }
	}

	return resp;
}
